/******************************************************************************
 *
 *  (C) Copyright 2004 Trend Micro, Inc.
 *
 *  All Rights Reserved.
 *  This program is an unpublished copyrighted work which is proprietary
 *  to Trend Micro, Inc. and contains confidential information that is not
 *  to be reproduced or disclosed to any other person or entity without
 *  prior written consent from Trend Micro, Inc. in each and every instance.
 *
 *  WARNING:  Unauthorized reproduction of this program as well as
 *  unauthorized preparation of derivative works based upon the
 *  program or distribution of copies by sale, rental, lease or
 *  lending are violations of federal copyright laws and state trade
 *  secret laws, punishable by civil and criminal penalties.
 *
 *****************************************************************************/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include "glcfg.h"

#define	MAXINTF		32
#define ETH		"eth"
#define MIN(x, y) ((x) < (y) ? (x) : (y))

#define GGB_CONF	"/etc/conf/ggb.conf"
#define PROG_VER	"/etc/program.ver"

#define MANGEMENT_PORT_ETH	(0) //eth0
#define OFFSET_TIME	(500) // milli-second

#define STR_LEN		(200)

enum {
	GET = 1,
	GETNEXT,
	SET
};

typedef long long	count_t;

struct perintf {
	int index;	/* save the index of eth0,eth1,etc */
	count_t rbyte;	/* number of read bytes */
};

struct intfstat {
	int		nrintf;
	struct perintf	intf[MAXINTF];
};

struct _oidmap {
	char *oid;
	int retType;
	int (*fn)(char**);
};

static void initNetDev(struct intfstat *intf)
{
	FILE *fp;
	char linebuf[1024];
	int i = 0;

	if ( (fp = fopen("/proc/net/dev", "r")) != NULL)
	{
		while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
		{
			if (NULL == strstr(linebuf,ETH))
				continue;

			sscanf(linebuf,	"%*3s %d: %lld %*d %*d %*d %*d %*d %*d "
					"%*d %*d %*d %*d %*d %*d %*d %*d %*d",
					&(intf->intf[i].index),
					&(intf->intf[i].rbyte));

			if (++i >= MAXINTF-1)
				break;
		}
		intf->nrintf = i;
		fclose(fp);
	}
}

static double accumulateThroughput(int exclude)
{
	int i=0;
	struct intfstat preintf,nextintf;
	count_t offset = 0;

	initNetDev(&preintf);
	usleep((unsigned long)OFFSET_TIME*1000);//microsecond
	initNetDev(&nextintf);

	for (;i< MIN(preintf.nrintf,nextintf.nrintf);i++)
	{
		if (i == exclude)
			continue;
		offset += (nextintf.intf[i].rbyte - preintf.intf[i].rbyte);
	}
	offset *= 8;
	return (double)offset/OFFSET_TIME;
}

static int getTotalDataPortthroughput(char **retStr)
{
	double offset= accumulateThroughput(MANGEMENT_PORT_ETH);

	if (offset < 1024)
	{
		snprintf(*retStr,STR_LEN,"%lld bps",(long long)offset);
		return 0;
	}

	if (offset < 1024*1024)
	{
		snprintf(*retStr,STR_LEN,"%.2f Kbps",offset / 1024);
		return 0;
	}

	if (offset < 1024*1024*1024)
	{
		snprintf(*retStr,STR_LEN,"%.2f Mbps", offset / 1024 / 1024);
		return 0;
	}

	snprintf(*retStr,STR_LEN,"%.2f Gbps",offset / 1024 / 1024 / 1024);

	return 0;
}

static int getProductVersion(char **retStr)
{
	//char *option;
	glcfg_t gconfig = NULL;
	const char *p = NULL;
	if ((gconfig = glcfg_open(PROG_VER, GLCFG_MODE_READ)) != NULL)
	{
		//option = NULL;
		p = glcfg_get_param_value(gconfig, "program", "version", NULL );
		if(p)
		{
			strncpy(*retStr, p ,STR_LEN-1);
		}
		glcfg_close(gconfig);
	}

	return 0;

}

static int getConcurrentConnections(char **retStr)
{
	FILE *fp;
	char linebuf[1024];
	unsigned long int conns = 0;
	char _unused[32], _unused1[64], _unused2[64];

	if ( (fp = fopen("/proc/ncit/meminfo", "r")) != NULL)
	{
		while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
		{
			if(strstr(linebuf, "conntrack_count:") != NULL) {
				sscanf(linebuf, "%s %ld (%s) %s", &_unused[0], &conns, &_unused1[0], &_unused2[0]);
				snprintf(*retStr, STR_LEN ,"%ld", conns);
				break;
			}
		}

		fclose(fp);
	}

	return 0;
}

static struct _oidmap oidmap[] = {
	{".1.3.6.1.4.1.6101.3003.1.0", 0, &(getProductVersion)},
	{".1.3.6.1.4.1.6101.3003.2.0", 0, &(getTotalDataPortthroughput)},
	{".1.3.6.1.4.1.6101.3003.4.0", 0, &(getConcurrentConnections)},
	/* keep for compatible with cn version */
	{".1.3.6.1.4.1.6101.3002.1.0", 0, &(getProductVersion)},
	{".1.3.6.1.4.1.6101.3002.2.0", 0, &(getTotalDataPortthroughput)},
	{".1.3.6.1.4.1.6101.3002.4.0", 0, &(getConcurrentConnections)},
};

const int MAXOID = sizeof(oidmap) / sizeof(struct _oidmap);

static void usage(void)
{
	int i;

	printf(
"Usage: tmSNMPQuery [OPTION] OID\n"
"  -g    GET\n"
"  -n    GETNEXT\n"
"\n"
"OID list:\n");

	for(i=0; i<MAXOID; i++) {
		printf("  %s\n", oidmap[i].oid);
	}
	printf("\n");
}

int main(int argc, char* argv[])
{
	int action;
	int i;


	if(argc != 3) {
		usage();
		return 0;
	}

	if(strcmp("-g", argv[1]) == 0)
		action = GET;
	if(strcmp("-n", argv[1]) == 0)
		action = GETNEXT;

	// Don't support snmpset
	if(strcmp("-s", argv[1]) == 0)
		return -1;

	char *retStr = (char *)malloc(STR_LEN);
	/* [2007-10-15 Sophie Chiang][TT 122937]
	 * Return simple variables.
	 * That is, replied OID is 2500.1.0, not 2500.1 (table format). */
	if (action == GET) {
		for (i = 0; i < MAXOID; i++) {
			if (strcmp(argv[2], oidmap[i].oid) == 0) {
				printf("%s\n", oidmap[i].oid);
				if (oidmap[i].retType) {
					printf("%s\n", "integer");
				} else {
					printf("%s\n", "string");
				}
				oidmap[i].fn(&retStr);
				printf("%s\n", retStr);
				break;
			}
		}
	} else if (action == GETNEXT) {
		for (i = 0; i < MAXOID; i++) {
			if (strcmp(argv[2], oidmap[i].oid) == 0 && (i + 1) < MAXOID) {//[strcmp]过来查询的OID完全和自身的OID相等，这时，返回下一个OID
				printf("%s\n", oidmap[i+1].oid);
				if (oidmap[i+1].retType) {
					printf("%s\n", "integer");
				} else {
					printf("%s\n", "string");
				}
				oidmap[i+1].fn(&retStr);
				printf("%s\n", retStr);
				break;
			} else if (strlen(argv[2]) < strlen(oidmap[i].oid) &&           // 过来查询的OID 仅仅和自身某个OID的一个前缀相等，此时，返回自身的OID
					strncmp(argv[2], oidmap[i].oid, strlen(argv[2])) == 0) {// [strncmp] input OID is a prefix-OID of ours, so return our OID
				printf("%s\n", oidmap[i].oid);
				if (oidmap[i].retType) {
					printf("%s\n", "integer");
				} else {
					printf("%s\n", "string");
				}
				oidmap[i].fn(&retStr);
				printf("%s\n", retStr);
				break;
			}
		}
	}

	free(retStr);
	return 0;
}